#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <termios.h>
#include <error.h>
#include <time.h>
#include "gsm0710.h"

#define AT_TIMEOUT_S(time) ((time)*1000)
#define AT_TIMEOUT_MS(time) (time)
#define AT_TIMEOUT_US(time) ((time)/1000)

struct at_init_table {
    const char *cmd;
    const char *res;
    const char *reslen;
    unsigned int t_ms; /* timeout @ millseconds */
};

/* Send AT command('cmd') and check the modem's responds. 
    if modem's responds contains 'res', return 1 else 0 .
    When 'tms' millseconds passed, haven't received 'res',
    this function will exit and return 0 means false 

    when GPRS modem powered on, it will send some garbage information.
    eg: after sending [ATE0\r], data received from serial port:
    51,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,fffffff6,70,
    d,a,4d,4f,44,45,4d,3a,53,54,41,52,54,55,50,d,a, [garbage]
    41,54,45,30,d,d,a,4f,4b,d,a, [ATE0\r\r\nOK\r\n] */
static int at_command(int fd,const char *cmd,
                const char *res,int resLen,int tms)
{
    char buf[256];
    int num,index=0;
    fd_set readfds;
    struct timeval tv;

    if(!cmd) return 0;
    write(fd,cmd,strlen(cmd)); /* send command */
    if(!res) return 0;

    /* read responds and checked */
    FD_ZERO(&readfds);
    FD_SET(fd,&readfds);
    tv.tv_sec= (tms/1000); /* get seconds part */
    tv.tv_usec=(tms%1000)*1000; /* get microseconds part */

    while(index<sizeof(buf))
    {
        switch(select(fd+1,&readfds,NULL,NULL,&tv))
        {
        case -1: /* select error */
        case  0: /* time is out */
            return 0; break;
        default: break;
        }
        num=read(fd,buf+index,sizeof(buf)-index);
        index+=num;
        if(index<resLen) continue;
        if(!strncmp(buf+index-resLen,res,resLen))
            return 1; /* find */
    }
    return 0;
}


int gsm0710_at_cmd_callback(struct gsm0710_context *ctx,const char *cmd)
{
    if(!at_command(ctx->fd,"ATE0\r","ATE0\r\r\nOK\r\n",11,
        AT_TIMEOUT_MS(1000))) return 0;
    /* Add your others initialize at command here... */
    /* ... */
    
    return at_command(ctx->fd,cmd,"\r\nOK\r\n",6,
            AT_TIMEOUT_MS(1000));
}

int gsm0710_read_callback(struct gsm0710_context *ctx, char *data, int len)
{
    return read(ctx->fd,data,len);
}

int 
gsm0710_write_callback(struct gsm0710_context *ctx,const char *data,int len)
{
    return write(ctx->fd,data,len);
}

void  gsm0710_deliver_data_callback(struct gsm0710_context *ctx,
    int channel,const char *data,int len)
{
    int i=0;
    printf("[channel:%d]",channel);
    for(;i<len;i++) printf("%x,",data[i]);
    putchar('\n');
}

void gsm0710_deliver_status_callback(struct gsm0710_context *ctx,
    int channel,int status)
{
	
}

/* register default callback function to gsm0710_context */
void gsm0710_callback_reg_dft(struct gsm0710_context *ctx,int serial_fd)
{
    ctx->fd = serial_fd;
    ctx->at_command =(gsm0710_context_at_command_callback)gsm0710_at_cmd_callback;
    ctx->read = (gsm0710_context_read_callback)gsm0710_read_callback;
    ctx->write =(gsm0710_context_write_callback)gsm0710_write_callback;
    ctx->deliver_data=
    	(gsm0710_context_deliver_data_callback)gsm0710_deliver_data_callback;
    ctx->deliver_status=
    	(gsm0710_context_deliver_status_callback)gsm0710_deliver_status_callback;
}

